package com.example.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.dto.*;
import com.example.entity.vo.request.AddCommentVO;
import com.example.entity.vo.request.TopicCreateVO;
import com.example.entity.vo.request.TopicUpdateVO;
import com.example.entity.vo.response.*;
import com.example.mapper.*;
import com.example.service.NotificationService;
import com.example.service.TopicService;
import com.example.utils.CacheUtils;
import com.example.utils.Const;
import com.example.utils.FlowUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.sql.Wrapper;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

@Service
@Slf4j
public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements TopicService {

    @Resource
    TopicTypeMapper topicTypeMapper;
    @Resource
    FlowUtils flowUtils;
    @Resource
    AccountMapper accountMapper;
    @Resource
    AccountDetailsMapper accountDetailsMapper;
    @Resource
    AccountPrivacyMapper accountPrivacyMapper;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    NotificationService notificationService;
    @Resource
    AmqpTemplate amqpTemplate;

    //评论的mapper
    @Resource
    TopicCommentMapper topicCommentMapper;

    @Override
    public List<TopicType> listTypes() {

        List<TopicType> topicTypes = topicTypeMapper.selectList(null);
        log.info("查询到的话题数量有：{}个", topicTypes.size());
        return topicTypes;
    }

    @Resource
    CacheUtils cacheUtils;

    @Override
    public String createTopic(int id, TopicCreateVO vo) {
        if (vo == null) {
            return "参数错误";
        } else if(vo.getTitle() == null || vo.getTitle().isEmpty()) {
            return "标题不能为空";
        } else if(vo.getTitle().length() > 50) {
            return "标题不能超过50个字符";
        } else if(vo.getContent() == null || vo.getContent().isEmpty()) {
            return "内容不能为空";
        } else if(!textLimitCheck(vo.getContent())) {
            return "内容不能超过20000个字符";
        } else if(topicTypeMapper.selectList(null).contains(vo.getType())) {
            return "话题类型不存在";
        }
//        String key = Const.FORUM_TOPIC_CREATE_COUNTER + id;
//        if (!flowUtils.limitPeriodCounterCheck(key, 1, 3600)) {
//            return "操作过于频繁";
//        }
        Topic topic = new Topic();
        topic.setContent(vo.getContent().toJSONString());
        topic.setTitle(vo.getTitle());
        topic.setType(vo.getType());
        topic.setUid(id);
        topic.setTime(new Date());
        if (this.save(topic)) {
            //删除缓存
            cacheUtils.deleteCachePattern(Const.FORUM_TOPIC_PREVIEW_CACHE + "*");
            log.info("用户{}创建了话题{}", id, vo.getTitle());
            return null;
        } else {
            log.error("用户{}创建话题{}失败", id, vo.getTitle());
            return "内部错误";
        }

    }

    @Override
    public List<TopicPreviewVO> listTopicByPage(int pageNumber, int type) {
        String key = Const.FORUM_TOPIC_PREVIEW_CACHE + pageNumber + ":" + type;
        List<TopicPreviewVO> list = cacheUtils.takeListFromCache(key, TopicPreviewVO.class);
        if (list != null) {
            log.info("从缓存中获取到了数据");
            return list;
        }
        Page<Topic> page = Page.of(pageNumber, 10);
        if (type == 0) {
            baseMapper.selectPage(page, Wrappers.<Topic>query().orderByDesc("time"));
        } else {
            baseMapper.selectPage(page, Wrappers.<Topic>query().eq("type", type).orderByDesc("time"));
        }
        List<Topic> topics = page.getRecords();
        if (topics.isEmpty()) {
            return null;
        }
        list = topics.stream().map(this::resolveToPreview).toList();
        cacheUtils.saveListToCache(key, list, 60);
        log.info("从数据库中获取到了数据, 并存入缓存");
        return list;
    }

    @Override
    public List<TopicTopVO> listTopTopics() {
        List<Topic> topics = baseMapper.selectList(Wrappers.<Topic>query().select("id", "title", "time").eq("top", 1));
        return topics.stream().map(topic -> {
            TopicTopVO vo = topic.asViewObject(TopicTopVO.class);
            return vo;
        }).toList();
    }

    @Override
    public TopicDetailVO getTopic(int tid, int uid) {
        TopicDetailVO vo = new TopicDetailVO();
        Topic topic = baseMapper.selectById(tid);
        if (topic == null) {
            return null;
        }
        vo.setId(topic.getId());
        vo.setType(topic.getType());
        vo.setTitle(topic.getTitle());
        vo.setTime(topic.getTime());
        vo.setContent(topic.getContent());
        TopicDetailVO.User user = new TopicDetailVO.User();
        vo.setUser(this.fillUserDetailsByPrivacy(user, topic.getUid()));
        TopicDetailVO.Interact interact = new TopicDetailVO.Interact(
                hasInteract(tid, uid, "like"),
                hasInteract(tid, uid, "collect")
        );
        vo.setInteract(interact);
        vo.setComments(topicCommentMapper.selectCount(Wrappers.<TopicComment>query().eq("tid", tid)));
        return vo;
    }

    private <T> T fillUserDetailsByPrivacy(T target, int uid) {

        if (target instanceof TopicDetailVO.User user) {
            AccountDetails details = accountDetailsMapper.selectById(uid);
            Account account = accountMapper.selectById(uid);
            AccountPrivacy privacy = accountPrivacyMapper.selectById(uid);
            user.setId(uid);
            if (privacy.isPhone()) {
                user.setPhone(details.getPhone());
            }
            if (privacy.isEmail()) {
                user.setEmail(account.getEmail());
            }
            if (privacy.isQq()) {
                user.setQq(details.getQq());
            }
            if (privacy.isWx()) {
                user.setWx(details.getWx());
            }
            user.setUsername(account.getUsername());
            user.setAvatar(account.getAvatar());
            user.setDesc(details.getDesc());
            if (privacy.isGender()) {
                user.setGender(details.getGender());
            }
        } else if(target instanceof CommentVO.User user) {
            AccountDetails details = accountDetailsMapper.selectById(uid);
            Account account = accountMapper.selectById(uid);
            AccountPrivacy privacy = accountPrivacyMapper.selectById(uid);
            user.setId(uid);
            if (privacy.isPhone()) {
                user.setPhone(details.getPhone());
            }
            if (privacy.isEmail()) {
                user.setEmail(account.getEmail());
            }
            if (privacy.isQq()) {
                user.setQq(details.getQq());
            }
            if (privacy.isWx()) {
                user.setWx(details.getWx());
            }
            user.setUsername(account.getUsername());
            user.setAvatar(account.getAvatar());
            if (privacy.isGender()) {
                user.setGender(details.getGender() == 1);
            }
        }
        return target;
    }

    private Boolean hasInteract(int tid, int uid, String type) {
        String key = tid + ":" + uid;
        if (stringRedisTemplate.opsForHash().hasKey(type, key)) {
            return Boolean.parseBoolean(stringRedisTemplate.opsForHash().entries(type).get(key).toString());
        }
        return baseMapper.userInteractCount(tid, uid, type) > 0;
    }

    //点赞与收藏相关业务interact，saveInteractSchedule，saveInteract
    @Override
    public void interact(Interact interact, boolean state) {
        this.sendRankDataToRabbit(interact, state);
        String type = interact.getType();
        synchronized (type.intern()) {
            stringRedisTemplate.opsForHash().put(type, interact.toKey(), Boolean.toString(state));
            this.saveInteractSchedule(type);
        }
    }

    //TODO 待学习JUV
    private final Map<String, Boolean> state = new HashMap<>();
    ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
    private void saveInteractSchedule(String type) {
        if(!state.getOrDefault(type, false)) {
            state.put(type, true);
            service.schedule(() -> {
                this.saveInteract(type);
                state.put(type, false);
            }, 3, TimeUnit.SECONDS);
        }
    }

    //向数据库写入数据
    private void saveInteract(String type) {
        synchronized (type.intern()) {
            List<Interact> check = new ArrayList<>();
            List<Interact> uncheck = new ArrayList<>();
            stringRedisTemplate.opsForHash().entries(type).forEach((key, value) -> {
                if (Boolean.parseBoolean(value.toString())) {
                    check.add(Interact.parseInteract(key.toString(), type));
                } else {
                    uncheck.add(Interact.parseInteract(key.toString(), type));
                }
            });
            if (!check.isEmpty()) {
                baseMapper.addInteract(check, type);
            }
            if (!uncheck.isEmpty()) {
                baseMapper.deleteInteract(uncheck, type);
            }
            //向数据库插入数据后，删除缓存
            stringRedisTemplate.delete(type);
        }
    }

    private void sendRankDataToRabbit(Interact interact, boolean state) {
        String key = Const.FORUM_RANK_INTERACT + ":" + interact.getType() + ":" + interact.getTid() + ":" + interact.getUid();
        String value = interact.getType().equals("like") ? (state ? "1" : "-1") : (state ? "2" : "-2");
        Map<String, Object> map = Map.of(key, value);
        amqpTemplate.convertAndSend(Const.FORUM_RANK, map);
        log.info("成功将消息发送到 rank 消息队列，key: {}, value: {}", key, value);
    }

    private TopicPreviewVO resolveToPreview(Topic topic) {
        TopicPreviewVO vo = new TopicPreviewVO();
        Account account = accountMapper.selectById(topic.getUid());
        if (account == null) {
            account = new Account();
            account.setUsername("该账户已注销");
            account.setUsername("已注销用户");
        }
        vo.setId(topic.getId());
        vo.setType(topic.getType());
        //查询点赞和收藏数量
        vo.setLike(baseMapper.interactCount(topic.getId(), "like"));
        vo.setCollect(baseMapper.interactCount(topic.getId(), "collect"));
        vo.setTitle(topic.getTitle());
        vo.setTime(topic.getTime());
        vo.setUid(topic.getUid());
        vo.setUsername(account.getUsername());
        vo.setAvatar(account.getAvatar());

        List<String> images = new ArrayList<>();
        StringBuilder previewText = new StringBuilder();
        JSONArray ops = JSONObject.parseObject(topic.getContent()).getJSONArray("ops");
        for (Object op : ops) {
            Object insert = JSONObject.from(op).get("insert");
            if (insert instanceof String) {
                if (previewText.length() > 300) {
                    continue;
                }
                previewText.append(insert);
            } else if (insert instanceof Map<?, ?> map) {
                Optional.ofNullable(map.get("image"))
                        .ifPresent(obj -> images.add(obj.toString()));
            }
        }
        vo.setText(previewText.length() > 300 ? previewText.substring(0, 300) : previewText.toString());
        vo.setImages(images);
        return vo;
    }

    private Boolean textLimitCheck(JSONObject object) {
        if (object == null) {
            return false;
        }
        long length = 0;
        for (Object op : object.getJSONArray("ops")) {
            length += JSONObject.from(op).getString("insert").length();
            if (length > 20000) {
                return false;
            }
        }
        log.info("文本长度检查通过");
        return true;
    }


    @Override
    public List<TopicPreviewVO> listTopicCollects(int uid) {
        return baseMapper.collectTopics(uid).stream().map(
                topic -> {
                    TopicPreviewVO vo = new TopicPreviewVO();
                    vo.setId(topic.getId());
                    vo.setType(topic.getType());
                    vo.setTitle(topic.getTitle());
                    vo.setTime(topic.getTime());
                    vo.setUid(topic.getUid());
                    vo.setLike(baseMapper.interactCount(topic.getId(), "like"));
                    vo.setCollect(baseMapper.interactCount(topic.getId(), "collect"));
                    Account account = accountMapper.selectById(topic.getUid());
                    vo.setUsername(account.getUsername());
                    vo.setAvatar(account.getAvatar());
                    return vo;
                }
        ).toList();
    }

    @Override
    public String updateTopic(int uid, TopicUpdateVO vo) {
        baseMapper.update(null, Wrappers.<Topic>update()
                .eq("uid", uid)
                .eq("id", vo.getId())
                .set("title", vo.getTitle())
                .set("content", vo.getContent().toString())
                .set("type", vo.getType())
        );
        return null;
    }

    @Override
    //发表评论
    public String createComment(AddCommentVO vo, int id) {
        TopicComment topicComment = new TopicComment();
        topicComment.setUid(id);
        topicComment.setTime(new Date());
        topicComment.setTid(vo.getTid());
        topicComment.setContent(vo.getContent());
        topicComment.setQuote(vo.getQuote());
        topicCommentMapper.insert(topicComment);
        Topic topic = baseMapper.selectById(vo.getTid());
        Account account = accountMapper.selectById(id);
        if (vo.getQuote() > 0) {
            TopicComment com = topicCommentMapper.selectById(vo.getQuote());
            if (!Objects.equals(account.getId(), com.getUid())) {
                log.info("用户{}回复了评论{}", id, vo.getQuote());
                notificationService.addNotification(
                        com.getUid(),
                        "您的评论被回复",
                        account.getUsername() + "回复了您的评论",
                        "success",
                        "/index/topic-detail/" + com.getTid()
                );
            }
        } else if (!Objects.equals(account.getId(), topic.getUid())) {
            notificationService.addNotification(
                    topic.getUid(),
                    "您有新的帖子回复",
                    account.getUsername()+" 回复了你发表主题: "+topic.getTitle()+"，快去看看吧！",
                    "success", "/index/topic-detail/"+topic.getId()
            );
        }
        return null;
    }

    //评论展示
    @Override
    public List<CommentVO> comments(int tid, int pageNumber) {
        Page<TopicComment> page = Page.of(pageNumber, 10);
        topicCommentMapper.selectPage(page, Wrappers.<TopicComment>query().eq("tid", tid));
        return page.getRecords().stream().map(dto -> {
            CommentVO vo = new CommentVO();
            vo.setId(dto.getId());
            vo.setTime(dto.getTime());
            vo.setContent(dto.getContent());
            if (dto.getQuote() > 0) {
                TopicComment comment = topicCommentMapper.selectOne(Wrappers.<TopicComment>query().eq("id", dto.getQuote()));
                //引用的评论可能已被删除
                if (comment != null) {
                    JSONObject object = JSONObject.parseObject(comment.getContent());
                    StringBuilder builder = new StringBuilder();
                    this.shortContent(object.getJSONArray("ops"), builder, obj -> {});
                    vo.setQuote(builder.toString());
                } else {
                    vo.setQuote("此评论已被删除");
                }
            }
            CommentVO.User user = new CommentVO.User();
            this.fillUserDetailsByPrivacy(user, dto.getUid());
            vo.setUser(user);
            return vo;
        }).toList();
    }


    private void shortContent(JSONArray ops, StringBuilder previewText, Consumer<Object> imageHandler) {
        for (Object op : ops) {
            Object insert = JSONObject.from(op).get("insert");
            if (insert instanceof String) {
                if (previewText.length() > 300) {
                    continue;
                }
                previewText.append(insert);
            } else if (insert instanceof Map<?, ?> map) {
                Optional.ofNullable(map.get("image")).ifPresent(imageHandler);
            }
        }

    }

    @Override
    //删除评论
    public void deleteComment(int id, int uid) {
        topicCommentMapper.delete(Wrappers.<TopicComment>query().eq("id", id).eq("uid", uid));
        log.info("用户{}删除了评论{}", uid, id);
    }
}
